let fs=require('fs')
let path=require('path')
//babylon   把源码解析成ast
//@babel/traverse
//@babel/types
//@babel/generator
let babylon=require('babylon')
let traverse=require('@babel/traverse').default
let generator=require('@babel/generator').default
let t=require('@babel/types')
let {main}=require('./main.js')
module.exports=class Compiler{
    constructor(config){
        this.config=config   //配置文件
        this.entry=config.entry
        this.entryId=''
        this.root=path.join(process.cwd())   //运行路径
        this.module={}  //模块依赖
    }
    run(){
      //构建依赖
      this.buildModule(path.join(this.root,this.entry),true)

      //输出打包后的文件
      this.emitFile()
    }
    buildModule(modulePath,isEntry){
      let source=this.getSource(modulePath,true)
      let moduleName='./'+path.relative(this.root,modulePath)
      let {sourceCode,dependencies}=this.parse(source,path.dirname(moduleName))
      this.module[moduleName]=sourceCode
      dependencies.forEach(item=>{
          this.buildModule(item,false)
      })
      if(isEntry){
          this.entryId=moduleName
      }
    }
    async emitFile(){
       let mainPath=path.join(this.config.output.path,this.config.output.filename)
       let mainStr=main(this.entryId,this.module)
       if(fs.existsSync(this.config.output.path)){
          await rmdir(this.config.output.path,{force:true})
       }
       fs.mkdirSync(this.config.output.path)
       fs.writeFileSync(mainPath,mainStr,'utf-8')

    }
    getSource(filepath){
       let source=fs.readFileSync(filepath,'utf-8')
       return source
    }
    parse(source,parentPath){
        let ast=babylon.parse(source)
        let dependencies=[]
        traverse(ast,{
            CallExpression(p){
                let node=p.node
                if(node.callee.name==='require'){
                    node.callee.name='__webpack_require__'
                    let moduleName=node.arguments[0].value
                    moduleName=moduleName+(path.extname(moduleName)?'':'.js')
                    moduleName='./'+path.join(parentPath,moduleName)
                    dependencies.push(moduleName)
                    node.arguments=[t.stringLiteral(moduleName)]
                }
            }
        }) 
        let sourceCode=generator(ast).code
        return{
            sourceCode:sourceCode,
            dependencies:dependencies
        }
    }
}

function rmdir(dir){

    if(fs.statSync(dir).isFile()){
        fs.unlinkSync(dir)
    }else{
        let files=fs.readdirSync(dir)
        files.forEach(item=>{
            rmdir(dir+'/'+item)
        })
        fs.rmdirSync(dir)
    }
   
}
